Skip to content

[FIX] databases: neutralize with the custom addons of the database - #180

Merged
brinkflew merged 2 commits into
betafrom
avs-neutralize-addons
Aug 6, 2026
Merged

[FIX] databases: neutralize with the custom addons of the database#180
brinkflew merged 2 commits into
betafrom
avs-neutralize-addons

Conversation

@brinkflew

Copy link
Copy Markdown
Contributor

Description

odev quickstart neutralized databases without ever running the neutralization scripts shipped by
their custom modules, and ran odoo-bin neutralize with an --addons-path that did not contain the
custom repository at all. Three independent defects had to line up for that:

  • The repository was linked too late. quickstart linked it after restoring the database, but
    neutralization runs from within restore, when the database has no repository yet and no addons
    path can be derived from it. It is now linked before the restore — and again after it, because
    restore drops and recreates the database, which deletes its row in the data store. Both calls
    are the same idempotent upsert.
  • The repository root is not an addons path. additional_addons_paths returned it as-is, so a
    repository keeping its modules in subdirectories was filtered out downstream and never reached
    --addons-path. The lookup for the directories actually holding modules existed only in
    OdoobinCommand, which odev neutralize does not inherit from. It now lives on OdoobinProcess
    as expand_addons_paths and is shared by both, deduplicating candidates before filtering them so
    the glob no longer runs once per module.
  • The custom scripts were looked up against the wrong names. Installed module names were
    intersected with the directory names of the addons paths. An addons path is a directory
    containing modules, never a module itself, so the intersection was always empty and the spinner
    reported "0 installed modules". The module map is now built from the subdirectories of each addons
    path, which also drops a nested loop that would have collected the same script once per addons
    path.

Also fixes an infinite loop in LocalDatabase.neutralize: the retry counter was reset by a walrus
assignment evaluated on every iteration of the loop it was guarding, so odev neutralize on a
database odev cannot start never returned. It has its own commit.

Note for reviewers

_additional_addons_paths defaults to None as a sentinel rather than [], so the expansion runs
once even when it legitimately finds nothing, and the property keeps handing back the same list
object — odev-plugin-ai-translation appends to it.

Making NeutralizeCommand an OdoobinCommand would also have fixed the addons path, and was
rejected: its __init__ chains into _set_addons_pathssave_database_repository, which can
prompt and rewrite the data store. That is far more behaviour than odev neutralize should carry.

Tests

New tests/tests/common/test_odoobin.py (expansion finds nested modules, ignores non-addons
directories, deduplicates, is computed once, and an explicit [] stays []), new
tests/tests/common/test_local_database.py (script collection and order, modules without a script
or not installed are skipped, and neutralize returns promptly when the process is unavailable), and
new tests/tests/commands/test_quickstart.py asserting the repository is set on the database handed
to restore and still set once the command returns — the guard against someone simplifying the
double link away. One fixture added, addon_01/data/neutralize.sql. Full test suite passes.

Linked Issues

Compliance

  • I have read the contribution guide
  • I made sure the documentation is up-to-date both in doctrings and the docs directory
  • I have added or modified unit tests where necessary
  • I have added new libraries to the requirements.txt file, if any
  • I have incremented the version number according the versioning guide
  • The PR contains my changes only and no other external commit

🤖 Generated with Claude Code

https://claude.ai/code/session_01K8csZBrrBYp8oqH5paxTAm

`odev quickstart` neutralized databases without ever running the neutralization
scripts shipped by their custom modules, and ran `odoo-bin neutralize` with an
`--addons-path` that did not contain the custom repository at all. Three separate
defects had to line up for that:

- `quickstart` linked the repository to the new database *after* restoring it,
  but neutralization runs from within `restore`. At that point the database had
  no repository, so no addons path could be derived from it. The repository is
  now linked before the restore, and again after it, since `restore` drops and
  recreates the database and clears its entry in the data store.

- `additional_addons_paths` returned the repository root as-is. A repository
  keeping its modules in subdirectories is not itself a valid addons path, so it
  was filtered out downstream and never reached `--addons-path`. The lookup for
  the directories actually holding modules only existed in `OdoobinCommand`,
  which `odev neutralize` does not inherit from; it now lives on `OdoobinProcess`
  as `expand_addons_paths` and is shared by both.

- The neutralization scripts of custom modules were looked up by intersecting the
  installed module names with the *directory names of the addons paths*. An addons
  path is a directory containing modules, never a module itself, so the
  intersection was always empty and the spinner reported "0 installed modules".
  The lookup now builds the module map from the subdirectories of each addons path.

Also fixes an infinite loop in `LocalDatabase.neutralize`: the retry counter was
reset by a walrus assignment evaluated on every iteration of the loop it was
guarding, so `odev neutralize` on a database odev cannot start never returned.

Closes #98
@brinkflew
brinkflew requested a review from lse-odoo July 27, 2026 00:40
lse-odoo
lse-odoo previously approved these changes Jul 31, 2026

@lse-odoo lse-odoo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM thaks for the fix

Comment thread odev/common/odoobin.py

self._additional_addons_paths: list[Path] = []
"""List of additional addons paths to use when starting the Odoo process."""
self._additional_addons_paths: list[Path] | None = None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: real sentinel values was introduced in python 3.15, see: https://peps.python.org/pep-0661/#rationale

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know if we ever drop support for the precedent versions later

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants